/*------------------------------------------------------------------------------
SIO.C:  Serial Communication Routines.

Copyright 1995-2002 KEIL Software, Inc.
------------------------------------------------------------------------------*/

#include <reg51.h>
#include <string.h>
#include "sio.h"
#include <stdio.h>

/*------------------------------------------------------------------------------
_getkey waits until a character is received from the serial port.  This may not
be the exact desired operation (for example if the buffer is empty, this
function hangs waiting for a character to be received).
------------------------------------------------------------------------------*/
char _getkey (void)
{
	int k;

	do
		{
		k = com_getchar ();
		}
	while (k == -1);

	return ((unsigned char) k);
}

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
char putchar (char c)
{
	volatile unsigned int i;

	while (com_putchar (c) != 0)
		{
		for (i=0; i<1000; i++)
			{
			/*** DO NOTHING ***/
			}
		}

	return (c);
}

/*------------------------------------------------------------------------------
Notes:

The length of the receive and transmit buffers must be a power of 2.

Each buffer has a next_in and a next_out index.

If next_in = next_out, the buffer is empty.

(next_in - next_out) % buffer_size = the number of characters in the buffer.
------------------------------------------------------------------------------*/
#define TBUF_SIZE   16           /*** Must be one of these powers of 2 (2,4,8,16,32,64,128) ***/
#define RBUF_SIZE   16           /*** Must be one of these powers of 2 (2,4,8,16,32,64,128) ***/

#define TBUF_SPACE  idata       /*** Memory space where the transmit buffer resides ***/
#define RBUF_SPACE  idata       /*** Memory space where the receive buffer resides ***/

#define CTRL_SPACE  data        /*** Memory space for the buffer indexes ***/

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
#if TBUF_SIZE < 2
#error TBUF_SIZE is too small.  It must be larger than 1.
#elif TBUF_SIZE > 128
#error TBUF_SIZE is too large.  It must be smaller than 129.
#elif ((TBUF_SIZE & (TBUF_SIZE-1)) != 0)
#error TBUF_SIZE must be a power of 2.
#endif

#if RBUF_SIZE < 2
#error RBUF_SIZE is too small.  It must be larger than 1.
#elif RBUF_SIZE > 128
#error RBUF_SIZE is too large.  It must be smaller than 129.
#elif ((RBUF_SIZE & (RBUF_SIZE-1)) != 0)
#error RBUF_SIZE must be a power of 2.
#endif

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
static TBUF_SPACE unsigned char tbuf [TBUF_SIZE];
static RBUF_SPACE unsigned char rbuf [RBUF_SIZE];

static CTRL_SPACE unsigned char t_in = 0;
static CTRL_SPACE unsigned char t_out = 0;

static CTRL_SPACE unsigned char r_in = 0;
static CTRL_SPACE unsigned char r_out = 0;

static bit ti_restart = 0;  /* NZ if TI=1 is required */


/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
static void com_isr (void) interrupt 4
{
	/*------------------------------------------------
	Received data interrupt.
	------------------------------------------------*/
	if (RI != 0)
		{
		RI = 0;

		if (((r_in - r_out) & ~(RBUF_SIZE-1)) == 0)
			{
			rbuf [r_in & (RBUF_SIZE-1)] = SBUF;
			r_in++;
			}
		}

	/*------------------------------------------------
	Transmitted data interrupt.
	------------------------------------------------*/
	if (TI != 0)
		{
		TI = 0;

		if (t_in != t_out)
			{
			SBUF = tbuf [t_out & (TBUF_SIZE-1)];
			t_out++;
			ti_restart = 0;
			}
		else
			{
			ti_restart = 1;
			}
		}
}

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
#pragma disable

void com_initialize (void)
{
	/*------------------------------------------------
	Setup TIMER1 to generate the proper baud rate.
	------------------------------------------------*/
	com_baudrate (9600);

	/*------------------------------------------------
	Clear com buffer indexes.
	------------------------------------------------*/
	t_in = 0;
	t_out = 0;

	r_in = 0;
	r_out = 0;

	RI = 0;             /* clear receiver interrupt */
	TI = 0;             /* clear transmit interrupt */
	ti_restart = 1;

	ES = 1;             /* enable serial interrupts */
}

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
#pragma disable

sfr CKCON = 0x8E;                     // Clock Control

void com_baudrate (
  unsigned long baudrate)
{
	/*------------------------------------------------
	Clear transmit interrupt and buffer.
	------------------------------------------------*/
	TI = 0;             /* clear transmit interrupt */
	t_in = 0;           /* empty transmit buffer */
	t_out = 0;

	/*------------------------------------------------
	Set timer 1 up as a baud rate generator.
	------------------------------------------------*/
	TR1 = 0;            /* stop timer 1 */
	ET1 = 0;            /* disable timer 1 interrupt */

  SCON = 0x10;                       // SCON0: 8-bit variable bit rate, level of STOP bit is ignored, RX enabled
                                       //        ninth bits are zeros, clear RI0 and TI0 bits
   if (SYSCLK/baudrate/2/256 < 1) {
      TH1 = -(SYSCLK/baudrate/2);
      CKCON &= ~0x0B;                  // T1M = 1; SCA1:0 = xx
      CKCON |=  0x08;
   } else if (SYSCLK/baudrate/2/256 < 4) {
      TH1 = -(SYSCLK/baudrate/2/4);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 01
      CKCON |=  0x01;
   } else if (SYSCLK/baudrate/2/256 < 12) {
      TH1 = -(SYSCLK/baudrate/2/12);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 00
   } else {
      TH1 = -(SYSCLK/baudrate/2/48);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 10
      CKCON |=  0x02;
   }

   TL1 = TH1;                          // init Timer1
   TMOD &= ~0xf0;                      // TMOD: timer 1 in 8-bit autoreload
   TMOD |=  0x20;
   TR1 = 1;                            // START Timer1
   IP |= 0x10;                         // Make UART high priority
   ES = 1;                            // Enable UART0 interrupts
}

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
#pragma disable

char com_putchar (
  unsigned char c)
{
	/*------------------------------------------------
	If the buffer is full, return an error value.
	------------------------------------------------*/
	if (com_tbuflen () >= TBUF_SIZE)
		return (-1);

	/*------------------------------------------------
	Add the data to the transmit buffer.  If the
	transmit interrupt is disabled, then enable it.
	------------------------------------------------*/
	tbuf [t_in & (TBUF_SIZE - 1)] = c;
	t_in++;

	if (ti_restart)
		{
		ti_restart = 0;
		TI = 1;               /* generate transmit interrupt */
		}

	return (0);
}

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
#pragma disable

int com_getchar (void)
{
	if (com_rbuflen () == 0)
		return (-1);

	return (rbuf [(r_out++) & (RBUF_SIZE - 1)]);
}

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
#pragma disable

unsigned char com_rbuflen (void)
{
	return (r_in - r_out);
}

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/
#pragma disable

unsigned char com_tbuflen (void)
{
	return (t_in - t_out);
}

#pragma disable
int com_write(unsigned char* buf, int size)
{
	int i;
	
	for(i=0; i < size; i++){
		com_putchar(buf[i]);
	}
	
	return size;
}

/*------------------------------------------------------------------------------
------------------------------------------------------------------------------*/

